package top.jacktgq.cas.unsafe;
import java.util.ArrayList;
import java.util.List;

import sun.misc.Unsafe;
/**
 * 
 * @Title: MyAtomicInteger.java 
 * @Package top.jacktgq.cas.unsafe 
 * @Description: 使用Unsafe对象，自定义我们自己的原子整数类
 * @author CandyWall   
 * @date 2020年11月3日 下午9:20:00 
 * @version V1.0
 */
public class MyAtomicInteger {
	private volatile int value;
	private static final long valueOffset;
	private static final Unsafe UNSAFE;
	static {
		UNSAFE = UnsafeAccessor.getUnsafe();
		//计算value域的偏移量
		try {
			valueOffset = UNSAFE.objectFieldOffset(MyAtomicInteger.class.getDeclaredField("value"));
		} catch (Exception e) {
			e.printStackTrace();
			throw new RuntimeException(e);
		}
	}
	
	public MyAtomicInteger(int value) {
		this.value = value;
	}

	public int getValue() {
		return value;
	}
	
	public void decrement(int amount) {
		while(true) {
			int prev = this.value;
			int next = prev - amount;
			if(UNSAFE.compareAndSwapInt(this, valueOffset, prev, next)) {
				break;
			}
		}
	}
	
	public static void main(String[] args) {
		Account.demo(new MyAccount(10000));
	}
}

class MyAccount implements Account {
	//使用自定义的原子整型进行多线程减法运算
	private MyAtomicInteger balance;

	public MyAccount(int balance) {
		this.balance = new MyAtomicInteger(balance);
	}

	@Override
	public Integer getBalance() {
		return balance.getValue();
	}

	@Override
	public void withdraw(int amount) {
		balance.decrement(amount);
	}
}

interface Account {
	//获取余额
	Integer getBalance();
	
	//取款
	void withdraw(int amount);
	
	/**
	 * 方法内会启动1000个线程，每个线程做-10元的操作，
	 * 如果初始余额为10000元，那么正确的结果应当是0
	 */
	static void demo(Account account) {
		List<Thread> threadList = new ArrayList<Thread>();
		for(int i = 0; i < 1000; i++) {
			threadList.add(new Thread(() -> {
				account.withdraw(10);
			}));
		}
		
		long start = System.currentTimeMillis();
		
		threadList.forEach(Thread :: start);
		threadList.forEach(t -> {
			try {
				t.join();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		});
		long end = System.currentTimeMillis();
		System.out.println("balance:" + account.getBalance() + ",cost:" + (end - start) + " ms");
		
	}
}
